有沒有發現我們之前的登入流程少了一個超重要的步驟?沒錯,就是密碼驗證!之前的範例中,我們根本沒檢查用戶的密碼,這樣根本不是一個完整的登入流程嘛~今天我們就來補上這個環節,教你如何在 MySQL 中安全儲存密碼,並在用戶登入時進行正確的密碼驗證,讓整個登入系統更完整、更安全!
千萬記住一件事:千萬不要直接儲存用戶的明文密碼!如果你的資料庫被入侵了,用戶的密碼就會立刻暴露,這樣就 GG 了,不僅損害用戶隱私,還可能引發更大的安全問題。為了避免這種情況,我們會對密碼進行加密後再儲存,並且在用戶登入時進行安全的比對。
要安全儲存密碼,我們可以使用 bcrypt 這個工具來加密。它有著很強的加密和鹽值功能,能夠防止暴力破解。這個工具可是每個開發者的好夥伴!
npm install bcrypt
當用戶註冊時,我們會先將用戶的密碼進行加密,再把加密後的密碼儲存到 MySQL 中。舉個例子,假設我們有一個 Users
表格,裡面有 email
和 password
這兩個欄位。
// controllers/authController.js
const bcrypt = require('bcrypt');
const db = require('../models'); // 引入你的 MySQL models
// 註冊用戶並加密密碼
exports.register = async (req, res) => {
const { email, password } = req.body;
try {
// 將密碼加密
const hashedPassword = await bcrypt.hash(password, 10); // 10 是 salt rounds 值,越高越安全,但越慢
// 儲存加密後的密碼到資料庫
await db.User.create({ email, password: hashedPassword });
res.json({ message: '註冊成功!' });
} catch (error) {
res.status(500).json({ message: '註冊失敗', error });
}
};
在這段程式中,我們將用戶密碼加密後儲存到 MySQL。這樣,即便資料庫被入侵,駭客也無法直接讀取到用戶的明文密碼,讓安全性大大提高!
接下來就是重點啦!當用戶登入時,我們需要將用戶輸入的密碼和資料庫裡儲存的加密密碼進行比較,這時候就可以用 bcrypt.compare 來幫忙。
// controllers/authController.js
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
// 登入並驗證密碼
exports.login = async (req, res) => {
const { email, password } = req.body;
try {
// 查找用戶
const user = await db.User.findOne({ where: { email } });
if (!user) {
return res.status(404).json({ message: '用戶不存在' });
}
// 驗證密碼
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) {
return res.status(401).json({ message: '密碼錯誤' });
}
// 密碼正確,生成 JWT
const token = jwt.sign({ userId: user.id }, process.env.JWT_SECRET, { expiresIn: '1h' });
res.json({ token });
} catch (error) {
res.status(500).json({ message: '登入失敗', error });
}
};
這段程式會在登入時先檢查用戶是否存在,再用 bcrypt 比對密碼。如果密碼正確,接下來就生成 JWT,回傳給前端。這樣,前端就可以攜帶 JWT 進行身份驗證啦!
登入流程總結起來就是這幾步:
這樣,我們就完成了密碼驗證與 JWT 生成的整合,整個流程既安全又有效率。
以下是註冊與登入的完整範例:
// controllers/authController.js
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const db = require('../models'); // 假設我們的資料庫 models
// 用戶註冊
exports.register = async (req, res) => {
const { email, password } = req.body;
try {
const hashedPassword = await bcrypt.hash(password, 10);
await db.User.create({ email, password: hashedPassword });
res.json({ message: '註冊成功!' });
} catch (error) {
res.status(500).json({ message: '註冊失敗', error });
}
};
// 用戶登入
exports.login = async (req, res) => {
const { email, password } = req.body;
try {
const user = await db.User.findOne({ where: { email } });
if (!user) {
return res.status(404).json({ message: '用戶不存在' });
}
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) {
return res.status(401).json({ message: '密碼錯誤' });
}
const token = jwt.sign({ userId: user.id }, process.env.JWT_SECRET, { expiresIn: '1h' });
res.json({ token });
} catch (error) {
res.status(500).json({ message: '登入失敗', error });
}
};
現在我們把這個流程整合到前後端,實現完整的登入流程。
前端 login.component.ts
:
import { AuthService } from './auth.service';
import { Router } from '@angular/router';
export class LoginComponent {
email: string = '';
password: string = '';
constructor(private authService: AuthService, private router: Router) {}
login() {
this.authService.login(this.email, this.password).subscribe(
(response) => {
localStorage.setItem('token', response.token); // 儲存 JWT
this.router.navigate(['/home']); // 登入成功後跳轉至首頁
},
(error) => {
console.error('登入失敗', error);
}
);
}
}
這樣一來,我們的前後端登入流程就完整實現啦!當用戶登入後,JWT 就會保存在 Local Storage 中,並且會自動附帶到每次 API 請求的標頭裡,確保每個請求都會攜帶正確的身份驗證資訊。
今天我們總算補齊了登入流程中最關鍵的部分——密碼驗證。從如何加密儲存密碼,到登入時比對正確的密碼,整個流程都得到完整的處理。而且還將 JWT 驗證結合起來,實現了我們完整的身份驗證系統。現在你的用戶資料安全大大提升,密碼再也不是薄弱環節!
這次我們經歷了: